1. Field of the Invention
The present invention is directed to technology for evaluating expressions.
2. Description of the Related Art
The specification of a computer program (e.g., source code) includes expressions that can be evaluated at different times, including expressions that should be evaluated when a program is compiled and expressions that should be evaluated when the program is executed. Many program languages use the same syntax to describe expressions that should be evaluated when a program is compiled and expressions that should be evaluated when a program is executed. In these languages, the syntactic context of the expression determines the expression's evaluation time. Macros, inlined functions, staged programming, templates, and compile-time specialization are standard for achieving this effect. The Common Lisp eval-when construct specifies whether an expression should be evaluated when the program is compiled or when it is loaded. These solutions control whether an expression is evaluated when a program is compiled or when it is executed.
Event-driven evaluation is typically achieved by placing an expression in different syntactic context depending on when it should be evaluated. An expression inside a function can be evaluated multiple times, once each time the function is called. An expression in the initializer of a global variable is executed once when the program is loaded. To use the first syntax to cause reevaluation in response to an event, the expression is wrapped in a function definition and the function is registered as an event handler. This is verbose and makes it hard to express this portion of a program's design. It creates a syntactic distinction between two very similar specifications, such that a minor change in the specification can lead to large changes in the design. Furthermore, it creates a disincentive to use designs that call for expressions that are re-evaluated.
For example, a value is defined in JavaScript as follows:view.width=100;Here's the code that access that value. This code reads the value, adds ten to it, and binds a variable ‘mywidth’ to it:var mywidth=view.width+10;
To have this value initialized immediately, when the program is loaded, the function F that contains the line “view.width=100” should be called once when the program is loaded. To change the program so that the value is initialized at a later time, the call to F should be moved to a location that is called later. Making this change requires global program analysis to inspect when F is called, when its caller F′ is called, and so on. It may also require refactoring the program.
For example, if F contains the lines:view.x=10;view.width=100;and it's necessary to make the initialization of view.width occur at a different time from the initialization of view.x, then F needs to be split into two functions, F0 which initializes view.x (and other variables that should be initialized at the first time), and F1 which initializes view.width (and other variables that should be initialized at the later time). Then the programmer must find a location in the source text at which to insert a call to F1. As can be seen, existing means to control the timing for evaluation for an expression is not sufficient.
The specification of a computer program also generally includes a set of relations between variables. For example, in procedural programming languages such as C, Java and JavaScript, these relations are encoded as statements that compute the values of expressions and reassign values to variables based on the computations. Ensuring that a variable is updated when a depended on value changes requires manual coding, and is verbose and error prone.
The Eiffel programming language, the Z assertion language, and extensions to Java such as iContract, allow the programmer to attach equation descriptions of invariants to program constructs. These are used to check the behavior of the program, but cannot automatically trigger the execution of update routines.
The Listener design pattern, created in the context of the Smalltalk, Model-View-Control architecture, standardizes a pattern for broadcasting notifications that a value has changed, and registering code that is executed when a notification is received. The sending of notifications, and the construction of update code that is executed when a notification is received and code that registers this update code is manual, verbose and error prone.
Some logic programming languages and constraint systems, such as Prolog, EQL, Ops5, and the Screamer extensions to Common Lisp have “logic variables.” A logic variable allows the expression that is used to compute the value of a variable to be placed in the source text next to the name of the variable, but evaluated when the values of the terms used in the expression are known. The expression is evaluated once, when the values of all the terms are bound (or initialized). Once a logic variable is bound to a value, its value never changes. These systems that use logic variable require either an execution engine with built-in support for logic variables, or a pre-processing stage where each expression in the program is replaced by a less efficient expression that deals with the possibility of logic variables. The first approach doesn't work for existing mainstream execution engines such as the Java Virtual Machine. The second approach is expensive both in computation time and the efficiency of the compiled program.
Thus, there is a need to provide a more useful and efficient means for identifying when an expression should be evaluated during execution and for implementing dependent relations between variables whose consistency is necessary for program correctness.